<?php
declare(strict_types=1);

namespace NanQi\Hope\Service;

use Hyperf\Contract\ContainerInterface;
use Hyperf\Contract\NormalizerInterface;
use Hyperf\Di\MethodDefinitionCollectorInterface;
use NanQi\Hope\Base\BaseService;

class CallService extends BaseService {

    /** @var MethodDefinitionCollectorInterface */
    private $methodDefinition;

    /** @var ContainerInterface */
    private $container;

    /** @var NormalizerInterface */
    private $normalizer;

    /**
     * CallService constructor.
     * @param MethodDefinitionCollectorInterface $methodDefinition
     * @param ContainerInterface $container
     * @param NormalizerInterface $normalizer
     */
    public function __construct(
        MethodDefinitionCollectorInterface $methodDefinition,
        ContainerInterface $container,
        NormalizerInterface $normalizer)
    {
        $this->methodDefinition = $methodDefinition;
        $this->container        = $container;
        $this->normalizer       = $normalizer;
    }


    /**
     * 依赖注入调用
     * @param string $className
     * @param string $method
     * @param array $arguments
     * @return mixed
     */
    public function call_di(string $className, string $method, array $arguments)
    {
        $instance = $this->container->get($className);

        $definitions = $this->methodDefinition->getParameters($className, $method);
        $parameters = [];
        foreach ($definitions ?? [] as $pos => $definition) {
            $value = $arguments[$pos] ?? $arguments[$definition->getMeta('name')] ?? null;
            if ($value === null) {
                if ($definition->getMeta('defaultValueAvailable')) {
                    $parameters[] = $definition->getMeta('defaultValue');
                } elseif ($definition->allowsNull()) {
                    $parameters[] = null;
                } elseif ($this->container->has($definition->getName())) {
                    $parameters[] = $this->container->get($definition->getName());
                } else {
                    throw new \InvalidArgumentException("Parameter '{$definition->getMeta('name')}' "
                        . "of {$className}:{$method} should not be null");
                }
            } else {

                $parameters[] = $this->normalizer->denormalize($value, $definition->getName());
            }
        }

        return $instance->{$method}(...$parameters);
    }
}
